#include <stdio.h>  
#include <unistd.h>  
#include <fcntl.h>  
#include <sys/mman.h>  
#include <pthread.h>
#include <malloc.h>
#include <QDebug>

#include "/home/ysy/QT/VDMA.h"

/* Register offsets */  
#define OFFSET_PARK_PTR_REG                     0x28  
#define OFFSET_VERSION                          0x2c  
  
#define OFFSET_VDMA_MM2S_CONTROL_REGISTER       0x00  
#define OFFSET_VDMA_MM2S_STATUS_REGISTER        0x04  
#define OFFSET_VDMA_MM2S_REG_INDEX              0x14 
#define OFFSET_VDMA_MM2S_VSIZE                  0x50  
#define OFFSET_VDMA_MM2S_HSIZE                  0x54  
#define OFFSET_VDMA_MM2S_FRMDLY_STRIDE          0x58  
#define OFFSET_VDMA_MM2S_FRAMEBUFFER1           0x5c  
#define OFFSET_VDMA_MM2S_FRAMEBUFFER2           0x60  
#define OFFSET_VDMA_MM2S_FRAMEBUFFER3           0x64  
#define OFFSET_VDMA_MM2S_FRAMEBUFFER4           0x68  
  
#define OFFSET_VDMA_S2MM_CONTROL_REGISTER       0x30  
#define OFFSET_VDMA_S2MM_STATUS_REGISTER        0x34  
#define OFFSET_VDMA_S2MM_IRQ_MASK               0x3c  
#define OFFSET_VDMA_S2MM_REG_INDEX              0x44  
#define OFFSET_VDMA_S2MM_VSIZE                  0xa0  
#define OFFSET_VDMA_S2MM_HSIZE                  0xa4  
#define OFFSET_VDMA_S2MM_FRMDLY_STRIDE          0xa8  
#define OFFSET_VDMA_S2MM_FRAMEBUFFER1           0xac  
#define OFFSET_VDMA_S2MM_FRAMEBUFFER2           0xb0  
#define OFFSET_VDMA_S2MM_FRAMEBUFFER3           0xb4  
#define OFFSET_VDMA_S2MM_FRAMEBUFFER4           0xb8  
  
/* S2MM and MM2S control register flags */  
#define VDMA_CONTROL_REGISTER_START                     0x00000001  
#define VDMA_CONTROL_REGISTER_CIRCULAR_PARK             0x00000002  
#define VDMA_CONTROL_REGISTER_RESET                     0x00000004  
#define VDMA_CONTROL_REGISTER_GENLOCK_ENABLE            0x00000008  
#define VDMA_CONTROL_REGISTER_FrameCntEn                0x00000010  
#define VDMA_CONTROL_REGISTER_INTERNAL_GENLOCK          0x00000080  
#define VDMA_CONTROL_REGISTER_WrPntr                    0x00000f00  
#define VDMA_CONTROL_REGISTER_FrmCtn_IrqEn              0x00001000  
#define VDMA_CONTROL_REGISTER_DlyCnt_IrqEn              0x00002000  
#define VDMA_CONTROL_REGISTER_ERR_IrqEn                 0x00004000  
#define VDMA_CONTROL_REGISTER_Repeat_En                 0x00008000  
#define VDMA_CONTROL_REGISTER_InterruptFrameCount       0x00ff0000  
#define VDMA_CONTROL_REGISTER_IRQDelayCount             0xff000000  
  
/* S2MM status register */  
#define VDMA_STATUS_REGISTER_HALTED                     0x00000001  // Read-only  
#define VDMA_STATUS_REGISTER_VDMAInternalError          0x00000010  // Read or write-clear  
#define VDMA_STATUS_REGISTER_VDMASlaveError             0x00000020  // Read-only  
#define VDMA_STATUS_REGISTER_VDMADecodeError            0x00000040  // Read-only  
#define VDMA_STATUS_REGISTER_StartOfFrameEarlyError     0x00000080  // Read-only  
#define VDMA_STATUS_REGISTER_EndOfLineEarlyError        0x00000100  // Read-only  
#define VDMA_STATUS_REGISTER_StartOfFrameLateError      0x00000800  // Read-only  
#define VDMA_STATUS_REGISTER_FrameCountInterrupt        0x00001000  // Read-only  
#define VDMA_STATUS_REGISTER_DelayCountInterrupt        0x00002000  // Read-only  
#define VDMA_STATUS_REGISTER_ErrorInterrupt             0x00004000  // Read-only  
#define VDMA_STATUS_REGISTER_EndOfLineLateError         0x00008000  // Read-only  
#define VDMA_STATUS_REGISTER_FrameCount                 0x00ff0000  // Read-only  
#define VDMA_STATUS_REGISTER_DelayCount                 0xff000000  // Read-only  
  
  
#define framenum 4
  

  

void vdma_set(vdma_handle *handle, int num, unsigned int val) {
    handle->vdmaVirtualAddress[num>>2]=val;  //int* length of int = 4
}  
  
  
unsigned int vdma_get(vdma_handle *handle, int num) {
    return handle->vdmaVirtualAddress[num>>2];  
}  

  
int vdma_setup(	vdma_handle *handle, unsigned int baseAddr, int width, int height, int pixelLength, fb_addr *fbaddr) {
    qDebug() << "vdma_setup_ing";
    handle->baseAddr=baseAddr;  
    handle->width=width;  
    handle->height=height;  
    handle->pixelLength=pixelLength;  
    handle->fbLength=pixelLength*width*height;  
    qDebug() << "open";
    handle->vdmaHandler = open("/dev/mem", O_RDWR | O_SYNC);  
    qDebug() << "mmap0";
    handle->vdmaVirtualAddress = (unsigned int*)mmap(NULL, 65535, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)handle->baseAddr);  
    if(handle->vdmaVirtualAddress == MAP_FAILED) {  
        printf("vdmaVirtualAddress mapping for absolute memory access failed.\n");  
        return -1;  
    }  
  

    qDebug() << "mmap1";
	for(int i=0;i<framenum;i++){
        fbaddr[i].fbV = (unsigned int*)mmap(NULL, handle->fbLength, PROT_READ | PROT_WRITE, MAP_SHARED, handle->vdmaHandler, (off_t)fbaddr[i].fbP);
        qDebug() << "mmap1.."<< fbaddr[i].fbV;
		
		memset(fbaddr[i].fbV, 255, handle->width*handle->height*handle->pixelLength);
	}
	
    return 0;  
}  



void vdma_halt(vdma_handle *handle, fb_addr *fbaddr) {  
    vdma_set(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER, VDMA_CONTROL_REGISTER_RESET);  
    vdma_set(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER, VDMA_CONTROL_REGISTER_RESET);  
    msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
    munmap((void *)handle->vdmaVirtualAddress, 65535);  
	
	for(int i=0;i<framenum;i++){
        munmap((void *)fbaddr[i].fbV, handle->fbLength);
	}
	
    close(handle->vdmaHandler);  
}  
  

  

  
void vdma_status_dump(int status) {  
    if (status & VDMA_STATUS_REGISTER_HALTED) printf(" halted"); else printf("running");  
    if (status & VDMA_STATUS_REGISTER_VDMAInternalError) printf(" vdma-internal-error");  
    if (status & VDMA_STATUS_REGISTER_VDMASlaveError) printf(" vdma-slave-error");  
    if (status & VDMA_STATUS_REGISTER_VDMADecodeError) printf(" vdma-decode-error");  
    if (status & VDMA_STATUS_REGISTER_StartOfFrameEarlyError) printf(" start-of-frame-early-error");  
    if (status & VDMA_STATUS_REGISTER_EndOfLineEarlyError) printf(" end-of-line-early-error");  
    if (status & VDMA_STATUS_REGISTER_StartOfFrameLateError) printf(" start-of-frame-late-error");  
    if (status & VDMA_STATUS_REGISTER_FrameCountInterrupt) printf(" frame-count-interrupt");  
    if (status & VDMA_STATUS_REGISTER_DelayCountInterrupt) printf(" delay-count-interrupt");  
    if (status & VDMA_STATUS_REGISTER_ErrorInterrupt) printf(" error-interrupt");  
    if (status & VDMA_STATUS_REGISTER_EndOfLineLateError) printf(" end-of-line-late-error");  
    printf(" frame-count:%d", (status & VDMA_STATUS_REGISTER_FrameCount) >> 16);  
    printf(" delay-count:%d", (status & VDMA_STATUS_REGISTER_DelayCount) >> 24);  
    printf("\n");  
}  
  
void vdma_s2mm_status_dump(vdma_handle *handle) {  
    msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
    int status = vdma_get(handle, OFFSET_VDMA_S2MM_STATUS_REGISTER);  
    printf("S2MM status register (%08x):", status);  
    vdma_status_dump(status);  
}  
  
void vdma_mm2s_status_dump(vdma_handle *handle) {  
    msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
    int status = vdma_get(handle, OFFSET_VDMA_MM2S_STATUS_REGISTER);  
    printf("MM2S status register (%08x):", status);  
    vdma_status_dump(status);  
}  
  
void vdma_start_buffering_set(vdma_handle *handle, int MM2S_SS2M) {  
    int interrupt_frame_count = 1; 
    
    if(MM2S_SS2M==0){
        vdma_set(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER, VDMA_CONTROL_REGISTER_RESET);
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
         while((vdma_get(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER) & VDMA_CONTROL_REGISTER_RESET)==4){
             //printf("Waiting VDMA_CONTROL_REGISTER_RESET...\n");
             msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
             usleep(100);
         }
        vdma_set(handle, OFFSET_VDMA_MM2S_STATUS_REGISTER, 0); 
        vdma_set(handle, OFFSET_VDMA_MM2S_FRMDLY_STRIDE, 0);
        vdma_set(handle, OFFSET_VDMA_S2MM_IRQ_MASK, 0xf);  
        vdma_set(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER,  
        (interrupt_frame_count << 16) |  
        VDMA_CONTROL_REGISTER_START |  
        VDMA_CONTROL_REGISTER_FrameCntEn |
        VDMA_CONTROL_REGISTER_FrmCtn_IrqEn);  
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
        usleep(100);
        while((vdma_get(handle, OFFSET_VDMA_MM2S_CONTROL_REGISTER)&1)==0 || (vdma_get(handle, OFFSET_VDMA_MM2S_STATUS_REGISTER)&1)==1) {  
            //printf("Waiting for VDMA to start running...\n");
            msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
            usleep(100);
        }
    }
    else{
        // Reset VDMA  
        vdma_set(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER, VDMA_CONTROL_REGISTER_RESET); 
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
        // Wait for reset to finish  
        while((vdma_get(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER) & VDMA_CONTROL_REGISTER_RESET)==4){
            //printf("Waiting VDMA_CONTROL_REGISTER_RESET...\n");
            msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
            usleep(100);
        }
        // Clear all error bits in status register  
        vdma_set(handle, OFFSET_VDMA_S2MM_STATUS_REGISTER, 0); 
        vdma_set(handle, OFFSET_VDMA_S2MM_FRMDLY_STRIDE, 0); 
        // Do not mask interrupts  
        vdma_set(handle, OFFSET_VDMA_S2MM_IRQ_MASK, 0xf); 
        // Start both S2MM and MM2S in triple buffering mode  
        vdma_set(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER,  
        (interrupt_frame_count << 16) |  
        VDMA_CONTROL_REGISTER_START |  
        VDMA_CONTROL_REGISTER_FrameCntEn |
        VDMA_CONTROL_REGISTER_FrmCtn_IrqEn); 
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
        usleep(100);
          while((vdma_get(handle, OFFSET_VDMA_S2MM_CONTROL_REGISTER)&1)==0 || (vdma_get(handle, OFFSET_VDMA_S2MM_STATUS_REGISTER)&1)==1) {  
            //printf("Waiting for VDMA to start running...\n");
              msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
              usleep(100);
          }
    }

  

    
	
}

void vdma_start_buffering(vdma_handle *handle, fb_addr *fbaddr, int frame_id, int MM2S_SS2M) { 
    
    if(MM2S_SS2M==0){
        vdma_set(handle, OFFSET_VDMA_MM2S_REG_INDEX, 0); 
        vdma_set(handle, OFFSET_VDMA_MM2S_FRAMEBUFFER1, (unsigned int)fbaddr[frame_id].fbP);
        vdma_set(handle, OFFSET_PARK_PTR_REG, 0); 
        vdma_set(handle, OFFSET_VDMA_MM2S_FRMDLY_STRIDE, handle->width*handle->pixelLength); 
        vdma_set(handle, OFFSET_VDMA_MM2S_HSIZE, handle->width*handle->pixelLength); 
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
        vdma_set(handle, OFFSET_VDMA_MM2S_VSIZE, handle->height);  
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
    }
    else{  
        // Extra register index, use first 16 frame pointer registers  
        vdma_set(handle, OFFSET_VDMA_S2MM_REG_INDEX, 0);        
        // Write physical addresses to control register  
        vdma_set(handle, OFFSET_VDMA_S2MM_FRAMEBUFFER1, (unsigned int)fbaddr[frame_id].fbP);
        // Write Park pointer register  
        vdma_set(handle, OFFSET_PARK_PTR_REG, 0);  
        // Frame delay and stride (bytes)  
        vdma_set(handle, OFFSET_VDMA_S2MM_FRMDLY_STRIDE, handle->width*handle->pixelLength);  
        // Write horizontal size (bytes)  
        vdma_set(handle, OFFSET_VDMA_S2MM_HSIZE, handle->width*handle->pixelLength);  
        // Write vertical size (lines), this actually starts the transfer  
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
        vdma_set(handle, OFFSET_VDMA_S2MM_VSIZE, handle->height);     
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
    }
}

// 1 means running
int vdma_running(vdma_handle *handle, int MM2S_SS2M) {  
    // Check whether VDMA is running, that is ready to start transfers  
    if(MM2S_SS2M==0){
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
        return (vdma_get(handle, OFFSET_VDMA_MM2S_STATUS_REGISTER)&1)==0;  
        
    }
    else{
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
        return (vdma_get(handle, OFFSET_VDMA_S2MM_STATUS_REGISTER)&1)==0;  
        
    }
}  

//1 means transf done
int vdma_idle(vdma_handle *handle, int MM2S_SS2M) {  
    // Check whtether VDMA is transferring  
    if(MM2S_SS2M==0){
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
        return (vdma_get(handle, OFFSET_VDMA_MM2S_STATUS_REGISTER) & VDMA_STATUS_REGISTER_FrameCountInterrupt)!=0;  
    }
    else{
        msync(handle->vdmaVirtualAddress, 0xFF, MS_SYNC);
        return (vdma_get(handle, OFFSET_VDMA_S2MM_STATUS_REGISTER) & VDMA_STATUS_REGISTER_FrameCountInterrupt)!=0;  
    }
    
}  
  
